﻿using System;
using System.Diagnostics;
using System.Text;
using System.Web;
using System.Web.Security;
using UDS.Entity;
using UDS.Components;

namespace UDS
{
    public class SiteContext
    {
        private const string KeyInMemory = "{90cd0273-0101-4672-9f09-51eee3a0c168}";
        private const string SessionCookieName = "UDS_SessionID";
        private readonly HttpContext _httpContext;

        /// <summary>
        /// Gets an instance of <see cref="SiteContext"/> class.
        /// </summary>
        public static SiteContext Current
        {
            get
            {
                HttpContext httpContext = HttpContext.Current;
                if (httpContext != null)
                {
                    return (SiteContext)httpContext.Items[KeyInMemory];
                }
                return null;
            }
        }

        /// <summary>
        /// Gets the name of the user who is visiting the web site.
        /// </summary>
        public string UserName
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the id of the user who is visiting the web site.
        /// </summary>
        public int UserID
        {
            get;
            private set;
        }

        public Guid SessionID
        {
            get;
            private set;
        }

        /// <summary>
        /// Get or set the active node id
        /// </summary>
        public int ActiveNodeID
        {
            get
            {
                int activeNodeId;
                HttpCookie cookie = _httpContext.Request.Cookies["ActiveNodeID"];
                if (cookie == null || !int.TryParse(cookie.Value, out activeNodeId))
                {
                    activeNodeId = 0;
                }

                if (activeNodeId > 0)
                {
                    // Validate whether the node is correct
                    ClassInfo classInfo = Class.GetClass(activeNodeId, true);
                    if (classInfo == null)
                    {
                        throw new InvalidOperationException(string.Format("Invalid node id: {0}.", activeNodeId));
                    }

                    ClassType expectedType = UDSConfig.GetCurrentAppType();
                    if (expectedType != ClassType.Unknown && classInfo.Type != expectedType)
                    {
                        throw new InvalidOperationException(string.Format("The expected type is '{0}' other than '{1}.", expectedType, classInfo.Type));
                    }
                }
                return activeNodeId;
            }
            set
            {
                HttpCookie cookie = new HttpCookie("ActiveNodeID", value.ToString());
                cookie.Path = FormsAuthentication.FormsCookiePath;
                cookie.HttpOnly = true;
                _httpContext.Response.Cookies.Add(cookie);
            }
        }

        /// <summary>
        /// Gets a value indicating whether the current request has been authenticated.
        /// </summary>
        public bool IsAuthenticated
        {
            get { return _httpContext.Request.IsAuthenticated; }
        }

        /// <summary>
        /// ctor.
        /// </summary>
        private SiteContext(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            _httpContext = context;
        }


        /// <summary>
        /// This method creates an instance of <see cref="SiteContext"/> class, and stores it in <see cref="HttpContext.Items"/>.
        /// This method MUST be called on Application.BeginRequest.
        /// </summary>
        public static void Initialize(HttpContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
#if DEBUG
            TraceListenerCollection listeners = Debug.Listeners;
            if (listeners == null)
            {
                throw new InvalidOperationException("No listeners added to Debug.Listeners?");
            }

            if (listeners.Count == 0 || listeners[listeners.Count - 1].GetType() != typeof(AssetTraceListener))
            {
                listeners.Add(new AssetTraceListener());
            }
#endif

            context.Items[KeyInMemory] = new SiteContext(context);
        }

        public void ClientRefresh(bool endResponse)
        {
            _httpContext.Response.AddHeader("Refresh", "0");
            _httpContext.Response.Write(@"<script type=""text/javascript"">
//<!--
window.location.href = window.location.href;
//-->
</script>
");

            if (endResponse)
            {
                _httpContext.Response.End();
            }
        }

        public void Alert(string msg)
        {
            Alert(msg, null, false);
        }

        public void AlertException(string msg, Exception ex)
        {
            const string sep = "\r\n--------------------------------------------------\r\n";
            Alert(string.Format("{0}" + sep + "{1}", msg, ex));
        }

        /// <summary>
        /// Prompt a message box to the client with the specified message.
        /// </summary>
        /// <param name="msg">The message to prompt.</param>
        /// <param name="back">Whether to go back to the previous page.</param>
        public void Alert(string msg, bool back)
        {
            Alert(msg, "window.self.history.go(-1);", true);
        }

        public void AlertAndRedirect(string msg, string redirectUrl)
        {
            if (string.IsNullOrEmpty(redirectUrl))
            {
                throw new ArgumentNullException("redirectUrl");
            }

            string script = string.Concat("window.location.href = \"", HttpUtil.HtmlQuotationMarkEncode(redirectUrl, true), "\";");
            Alert(msg, script, true);
        }

        public void Alert(string msg, string additionaScript, bool endResponse)
        {
            if (string.IsNullOrEmpty(msg))
            {
                throw new ArgumentNullException("msg");
            }
            var sb = new StringBuilder();
            sb.AppendLine("<script type=\"text/javascript\">");
            sb.AppendLine("//<!--");

            sb.Append("alert(\"");
            sb.Append(HttpUtil.HtmlQuotationMarkEncode(msg, true));
            sb.AppendLine("\");");

            if (!string.IsNullOrEmpty(additionaScript))
            {
                // sb.AppendLine("window.self.history.go(-1);");
                sb.AppendLine(additionaScript);
            }
            sb.AppendLine("//-->");
            sb.AppendLine("</script>");

            _httpContext.Response.Write(sb.ToString());

            if (endResponse)
            {
                // Stop execution of the page.
                _httpContext.Response.End();
            }
        }

        public void LoginAndRedirect(int userId, string userName)
        {
            if (string.IsNullOrEmpty(userName))
            {
                throw new ArgumentNullException("userName");
            }
            if (userId <= 0)
            {
                throw new ArgumentOutOfRangeException("userId");
            }

            // Create a default authentication cookie
            HttpCookie cookie = FormsAuthentication.GetAuthCookie(userName, false);

            // Create a new ticket for storing the user id
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
            var ticketWithUserData = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userId.ToString());

            // Update the cookie, and send it to client.
            cookie.Value = FormsAuthentication.Encrypt(ticketWithUserData);
            _httpContext.Response.Cookies.Add(cookie);

            UserID = userId;
            UserName = userName;

            string redirectUrl = FormsAuthentication.GetRedirectUrl(userName, false);
            if (string.IsNullOrEmpty(redirectUrl))
            {
                redirectUrl = "~/SubModule/Index.aspx";
            }
            _httpContext.Response.Redirect(redirectUrl);
        }

        /// <summary>
        /// Read the authentication data. This method should be called after Application.AuthenticateRequest
        /// </summary>
        public void SetupIdentity()
        {
            if (_httpContext.Request.IsAuthenticated && _httpContext.User != null)
            {
                var identity = _httpContext.User.Identity;
                if (identity != null && identity is FormsIdentity)
                {
                    var formsIdentity = (FormsIdentity)identity;

                    UserName = formsIdentity.Name;
                    UserID = int.Parse(formsIdentity.Ticket.UserData);
                }
                else
                {
                    throw new InvalidOperationException("Only FORMS authentication is supported!");
                }
            }

            var cookie = _httpContext.Request.Cookies[SessionCookieName];
            var createCookie = true;
            string cookieValue = (cookie != null) ? cookie.Value : null;
            if (!string.IsNullOrEmpty(cookieValue))
            {
                try
                {
                    SessionID = new Guid(cookieValue);
                    createCookie = false;
                }
                catch
                {
                    // Failed to parse the session id
                }
            }

            if (createCookie)
            {
                SessionID = Guid.NewGuid();
                cookie = new HttpCookie(SessionCookieName, SessionID.ToString());
                cookie.HttpOnly = true;
                cookie.Path = "/";
                _httpContext.Response.Cookies.Add(cookie);
            }
        }

        /// <summary>
        /// Remove the session id cookie from the request.
        /// </summary>
        public void ResetSessionID()
        {
            _httpContext.Response.Cookies.Remove(SessionCookieName);
        }
    }
}